﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;

namespace Walson.IO
{
    class TimedBinaryStream : IDisposable
    {
        private System.Threading.Timer _IntervalTimer;
        private long _TimeOutValue;
        private int _Capacity;

        private MemoryStream _Ms;
        private BinaryReader _Br;
        private BinaryWriter _Bw;

        public delegate void StreamEmptyEventHandler(object sender);
        public event StreamEmptyEventHandler StreamEmptyOccured;

        public long Length
        {
            get
            {
                long len = 0;
                lock (_Ms)
                {
                    len = _Ms.Length - _Ms.Position;
                }
                return len;
            }
        }

        private long _TimeOut;
        public long TimeOut
        {
            get
            {
                return _TimeOut;
            }
            set
            {
                _TimeOut = value;
                _TimeOutValue = value;
                _IntervalTimer?.Change(value * 1000, value * 1000);
            }
        }

        //             public int ID
        //             {
        //                 get;
        //                 private set;
        //             }

        private void TimerTick(object state)
        {
            lock (_Ms)
            {
                _IntervalTimer.Change(-1, -1);
                StreamEmptyOccured?.Invoke(this);
            }
        }

        /// <summary>
        /// 带有超时的二进制流
        /// </summary>
        /// <param name="capacity">流的容量（字节）</param>
        /// <param name="timeout">超时时间（秒）</param>
        public TimedBinaryStream(int capacity, int timeout = 1)
        {
            _Ms = new MemoryStream();
            _Ms.Capacity = capacity;
            _Capacity = capacity;

            _Br = new BinaryReader(_Ms);
            _Bw = new BinaryWriter(_Ms);

            //ID = Guid.NewGuid().GetHashCode();

            _TimeOut = timeout;
            _TimeOutValue = timeout * 1000;
            _IntervalTimer = new Timer(new TimerCallback(TimerTick), null, _TimeOutValue, _TimeOutValue);
        }

        public TimedBinaryStream()
            : this(10240)
        {

        }

        public void Write(byte[] buffer, int offset, int count)
        {
            lock (_Ms)
            {
                long pos = _Ms.Position;
                _Ms.Position = _Ms.Length;
                _Bw.Write(buffer, offset, count);
                _Ms.Position = pos;
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
        }

        public void Write(byte[] buffer)
        {
            lock (_Ms)
            {
                long pos = _Ms.Position;
                _Ms.Position = _Ms.Length;
                _Bw.Write(buffer);
                _Ms.Position = pos;
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
        }

        public int Read(byte[] buffer, int count)
        {
            int n = 0;
            lock (_Ms)
            {
                n = _Br.Read(buffer, 0, count);
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return n;
        }



        public byte[] ReadBytes(int count)
        {
            byte[] bytes;
            lock (_Ms)
            {
                bytes = _Br.ReadBytes(count);
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return bytes;
        }

        public byte ReadByte()
        {
            byte b = 0;
            lock (_Ms)
            {
                b = _Br.ReadByte();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return b;
        }

        public UInt16 ReadUInt16()
        {
            UInt16 value = 0;
            lock (_Ms)
            {
                value = _Br.ReadUInt16();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public UInt32 ReadUInt32()
        {
            UInt32 value = 0;
            lock (_Ms)
            {
                value = _Br.ReadUInt32();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public UInt64 ReadUInt64()
        {
            UInt64 value = 0;
            lock (_Ms)
            {
                value = _Br.ReadUInt64();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public sbyte ReadSbyte()
        {
            sbyte value = 0;
            lock (_Ms)
            {
                value = _Br.ReadSByte();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public char ReadChar()
        {
            char value = '\0';
            lock (_Ms)
            {
                value = _Br.ReadChar();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public char[] ReadChars(int count)
        {
            char[] value = null;
            lock (_Ms)
            {
                value = _Br.ReadChars(count);
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public Int16 ReadInt16()
        {
            Int16 value = 0;
            lock (_Ms)
            {
                value = _Br.ReadInt16();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public Int32 ReadInt32()
        {
            Int32 value = 0;
            lock (_Ms)
            {
                value = _Br.ReadInt32();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public Int64 ReadInt64()
        {
            Int64 value = 0;
            lock (_Ms)
            {
                value = _Br.ReadInt64();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public double ReadDouble()
        {
            double value = 0;
            lock (_Ms)
            {
                value = _Br.ReadDouble();
                if (_Ms.Position == _Ms.Length)
                {
                    _Ms.SetLength(0);
                    if (_Ms.Capacity != _Capacity)
                    {
                        _Ms.Capacity = _Capacity;
                    }
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return value;
        }

        public byte[] ReadAll()
        {
            byte[] all = null;
            lock (_Ms)
            {
                all = new byte[_Ms.Length - _Ms.Position];
                _Br.Read(all, 0, all.Length);
                _Ms.SetLength(0);
                _Ms.Capacity = _Capacity;
                if (_Ms.Capacity != _Capacity)
                {
                    _Ms.Capacity = _Capacity;
                }
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
            return all;
        }

        public void Flush()
        {
            lock (_Ms)
            {
                _Bw.Flush();
            }

            _IntervalTimer.Change(_TimeOutValue, _TimeOutValue);
        }

        public void Dispose()
        {
            _IntervalTimer.Change(-1, -1);
            _IntervalTimer.Dispose();

            _Bw.Close();
            _Br.Close();
            _Ms.Close();

            _Bw = null;
            _Br = null;
            _Ms = null;
        }
    }
}
